﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using SlimDX;
using SlimDX.Direct3D10;
using ColladaLoader;


namespace SCS.GFX
{
  public class TexturedMeshObject : MeshObject 
  {

      // The variables below are used to update the shaders variables
      EffectResourceVariable FEffectResourceVariable; //A pointer to the shaders texture resource
      Texture2D FTexture; //The texture on our model (only 1 texture / mesh supported in this sample)
      ShaderResourceView FResourceView; //A view to that texture (resource), so the shader can access it


    private DepthStencilStateDescription  dssd = new DepthStencilStateDescription();
    private DepthStencilState dss;


    //private List<TextureViewPair> FTextureViews = new List<TextureViewPair>();




    public TexturedMeshObject(MeshContainer m) : base(m) 
    {

      dssd = new DepthStencilStateDescription();
      dssd.IsDepthEnabled = true;
      dssd.IsStencilEnabled = false;
      dssd.DepthWriteMask = DepthWriteMask.All;
      dssd.DepthComparison = Comparison.LessEqual;
      dss = DepthStencilState.FromDescription(Renderer.Device, dssd);

      //FEffect = Effect.FromFile(Renderer.Device, System.IO.Directory.GetCurrentDirectory() + "\\Effects\\PositionColored.fx", "fx_4_0", ShaderFlags.None, EffectFlags.None, null, null);
      FEffect = Effect.FromFile(Renderer.Device, "..\\..\\..\\..\\Effects\\PositionTextured.fx", "fx_4_0", ShaderFlags.None, EffectFlags.None, null, null);
      FTechnique = FEffect.GetTechniqueByIndex(0);
      //Get the first pass
      FPass = FTechnique.GetPassByIndex(0);

      FLayout = new InputLayout(Renderer.Device, PositionTextured.InputElements, FPass.Description.Signature);

      //Get some pointers to the shaders variables so we can update them when needed
      FEffectVariable = FEffect.GetVariableByName("Tex2D_0");
      FEffectResourceVariable = FEffectVariable.AsResource();

      FEffectVariable = FEffect.GetVariableByName("ViewProjection");
      FEffectViewProjectionMatrix = FEffectVariable.AsMatrix();

      FEffectVariable = FEffect.GetVariableByName("World");
      FEffectWorldMatrix = FEffectVariable.AsMatrix();

      FPosition = m.position;
      FObjectMatrix = m.objectMatrix;

      FWorldMatrix = FObjectMatrix * Matrix.Translation(FPosition);

      LoadTextures(m);
      LoadVertexBuffer(m);
      LoadIndexBuffer(m);



    }

    public override void LoadTextures(MeshContainer m) {
        //Extend this system to a list of textures if you want multitexturing
        if (m.textures.Count > 1)
            System.Console.WriteLine("Only 1 Texture per mesh supported, try a simpler mesh!");

        foreach (string s in m.textures)
        {
            string t = s;

            //Create the actual texture from a file
            FTexture = Texture2D.FromFile(Renderer.Device, "..\\..\\..\\..\\Models\\" + s);
            //Create the shader resourceview, needed to bind the texture to the shader
            FResourceView = new ShaderResourceView(Renderer.Device, FTexture);
        }
    }

    public override void LoadVertexBuffer(MeshContainer m) {

      FVerticeCount = m.vertices.Count;
      
      BufferDescription bufferDescription = new BufferDescription();
      bufferDescription.BindFlags = BindFlags.VertexBuffer;
      bufferDescription.CpuAccessFlags = CpuAccessFlags.None;
      bufferDescription.OptionFlags = ResourceOptionFlags.None;
      bufferDescription.SizeInBytes = FVerticeCount * PositionTextured.SizeInBytes;
      bufferDescription.Usage = ResourceUsage.Default;

      DataStream stream = new DataStream(FVerticeCount * PositionTextured.SizeInBytes, true, true);
      stream.Position = 0;
      foreach (SkinnedTangentVertex i in m.vertices) {
        stream.Write(new Vector3(i.pos.X, i.pos.Z, i.pos.Y));
        stream.Write(new Vector2(i.U, i.V));
      }
      stream.Position = 0;
      FVertexBuffer = new SlimDX.Direct3D10.Buffer(Renderer.Device, stream, bufferDescription);
      stream.Close();
    }

    public override void LoadIndexBuffer(MeshContainer m) {
      FIndexCount = m.indices.Length;
      
      DataStream indexstream = new DataStream(FIndexCount * 2, true, true);
      indexstream.Position = 0;

      for (int i = 0; i < m.indices.Length; i++) {
        indexstream.Write(m.indices[i]);
      }
      indexstream.Position = 0;

      BufferDescription indexBufferDescription = new BufferDescription();
      indexBufferDescription.BindFlags = BindFlags.IndexBuffer;
      indexBufferDescription.CpuAccessFlags = CpuAccessFlags.None;
      indexBufferDescription.OptionFlags = ResourceOptionFlags.None;
      indexBufferDescription.SizeInBytes = FIndexCount * 2;
      indexBufferDescription.Usage = ResourceUsage.Default;

      FIndexBuffer = new SlimDX.Direct3D10.Buffer(Renderer.Device, indexstream, indexBufferDescription);

    }

    public override void Render(RenderInfo aInfo) {
      
      Renderer.Device.OutputMerger.DepthStencilState = dss;

      //Set input layout and primitive topology
      Renderer.Device.InputAssembler.SetInputLayout(FLayout);
      Renderer.Device.InputAssembler.SetPrimitiveTopology(PrimitiveTopology.TriangleList);

      //Set vertex and index buffers
      Renderer.Device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(FVertexBuffer, PositionTextured.SizeInBytes, 0));
      Renderer.Device.InputAssembler.SetIndexBuffer(FIndexBuffer, SlimDX.DXGI.Format.R16_UInt, 0);

      //Set the shaders resource variable (aka bind the texture to the current shader effect)
      FEffectResourceVariable.SetResource(FResourceView);

      //Update the shaders World and ViewProjection Matrices
      FWorldMatrix = FObjectMatrix * Matrix.Translation(FPosition) * Matrix.Translation(FLocation);
        
      FEffectWorldMatrix.SetMatrix(FWorldMatrix);
      FEffectViewProjectionMatrix.SetMatrix(Renderer.View * Renderer.Projection);



      //Apply Shader pass(es)
      FPass.Apply();

      //Draw the mesh

      Renderer.Device.DrawIndexed(FIndexCount, 0, 0);



    }

    public override void Dispose() {
      base.Dispose();

			if (FEffect!= null)
				FEffect.Dispose();
			FEffect = null;
            if (FResourceView != null)
                FResourceView.Dispose();
            if (FTexture != null)
                FTexture.Dispose();

    }

  }
}
